//
//
// Tencent is pleased to support the open source community by making tRPC available.
//
// Copyright (C) 2023 THL A29 Limited, a Tencent company.
// All rights reserved.
//
// If you have downloaded a copy of the tRPC source code from Tencent,
// please note that tRPC source code is licensed under the  Apache 2.0 License,
// A copy of the Apache 2.0 License is included in this file.
//
//

package http

import (
	"context"
	"errors"
	"fmt"
	stdhttp "net/http"

	"trpc.group/trpc-go/trpc-go/server"
)

// ServiceDesc is descriptor for server.RegisterService.
var ServiceDesc = server.ServiceDesc{
	HandlerType: nil,
}

// Handle registers http handler with custom route.
func Handle(pattern string, h stdhttp.Handler) {
	handler := func(w stdhttp.ResponseWriter, r *stdhttp.Request) error {
		h.ServeHTTP(w, r)
		return nil
	}

	ServiceDesc.Methods = append(ServiceDesc.Methods, generateMethod(pattern, handler))
}

// HandleFunc registers http handler with custom route.
func HandleFunc(pattern string, handler func(w stdhttp.ResponseWriter, r *stdhttp.Request) error) {
	ServiceDesc.Methods = append(ServiceDesc.Methods, generateMethod(pattern, handler))
}

// RegisterDefaultService registers service.
// See http/README.md for usage details.
// Deprecated: use RegisterNoProtocolService(s.Service("your.stdhttp.service.name")) instead.
func RegisterDefaultService(s server.Service) {
	DefaultServerCodec.AutoReadBody = false
	RegisterNoProtocolService(s)
}

// RegisterNoProtocolService registers no protocol service.
// See http/README.md for usage details.
func RegisterNoProtocolService(s server.Service) {
	if err := s.Register(&ServiceDesc, nil); err != nil {
		panic(fmt.Sprintf("register http no protocol service fail, err: %+v", err))
	}
}

// RegisterServiceMux registers service with http standard mux handler.
// Business registers routing plug-in by himself.
// Deprecated: use RegisterNoProtocolServiceMux(s.Service("your.stdhttp.service.name"), mux) instead.
func RegisterServiceMux(s server.Service, mux stdhttp.Handler) {
	DefaultServerCodec.AutoReadBody = false
	RegisterNoProtocolServiceMux(s, mux)
}

// RegisterNoProtocolServiceMux registers service with http standard mux handler.
// Business registers routing plug-in by himself.
func RegisterNoProtocolServiceMux(s server.Service, mux stdhttp.Handler) {
	handler := func(w stdhttp.ResponseWriter, r *stdhttp.Request) error {
		mux.ServeHTTP(w, r)
		return nil
	}
	method := generateMethod("*", handler)
	var serviceDesc = server.ServiceDesc{
		HandlerType: nil,
		Methods:     []server.Method{method},
	}
	if err := s.Register(&serviceDesc, nil); err != nil {
		panic(fmt.Sprintf("register http no protocol service mux fail, err: %+v", err))
	}
}

// generateMethod generates server method.
func generateMethod(pattern string, handler func(w stdhttp.ResponseWriter, r *stdhttp.Request) error) server.Method {
	handlerFunc := func(_ interface{}, ctx context.Context, f server.FilterFunc) (rspBody interface{}, err error) {
		filters, err := f(nil)
		if err != nil {
			return nil, err
		}
		handleFunc := func(ctx context.Context, _ interface{}) (rspBody interface{}, err error) {
			head := Head(ctx)
			if head == nil {
				return nil, errors.New("http Handle missing http header in context")
			}
			req := head.Request.WithContext(ctx)
			rsp := head.Response
			err = handler(rsp, req)
			// Fix issues/778
			// Currently, Request.MultipartForm is nil if Request.ParseMultipartForm() is not called.
			// Then head.Request.WithContext cannot carry the corresponding Request.MultipartForm pointer to the user.
			// If the user parses the multipart form data inside the handler, req will have a valid MultipartForm pointer,
			// but this pointer is not going to be reflected in head.Request.
			// Consequence:
			//   The standard http library is unable to remove the temporary files generated by multipart form data.
			//
			// The code below attempts to pass the pointer req.MultipartForm to head.Request.MultipartForm (which is
			// a pointer the standard library code has access to). Thereafter temporary files generated by parsing
			// multipart form data is guaranteed to be deleted.
			if head.Request.MultipartForm == nil {
				head.Request.MultipartForm = req.MultipartForm
			}
			return nil, err
		}
		return filters.Filter(ctx, nil, handleFunc)
	}
	return server.Method{
		Name: pattern,
		Func: handlerFunc,
	}
}
